/*****************************************************************************
 * hal_esp32_uart.c
 *
 * Copyright (C) 2020 Jeasonvor 1101627719@qq.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 ****************************************************************************/

/*****************************************************************************
 * Included Files
 ****************************************************************************/
#include "plum_api.h"
#include "hal/source/hal_esp32/hal_esp32.h"
/*****************************************************************************
 * Trace Definitions
 ****************************************************************************/
#define LOG_RUN_LEVEL LOG_LEVEL_NONE
#define LOG_MODULE    "hal.uart"
#include "thirdparty/log/log.h"
/*****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/
#define HAL_ESP32_UART_PORT_DEFALT 0xFFFF
/*****************************************************************************
 * Private Types
 ****************************************************************************/

/*****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/*****************************************************************************
 * Private Data
 ****************************************************************************/

/*****************************************************************************
 * Public Data
 ****************************************************************************/

/*****************************************************************************
 * Private Functions
 ****************************************************************************/

PLUM_PRIVATE
uart_port_t hal_esp32_uart_port(plum_u32 id)
{
    uart_port_t port = HAL_ESP32_UART_PORT_DEFALT;

    switch (PLUM_HAL_UART_IDX(id)) {
#if defined(UART_NUM_0)
        case 0:
            port = UART_NUM_0;
            break;
#endif
#if defined(UART_NUM_1)
        case 1:
            port = UART_NUM_1;
            break;
#endif
#if defined(UART_NUM_2)
        case 2:
            port = UART_NUM_2;
            break;
#endif
        default:
            LOG_E("eps32 not support the uart port:uart[%d]",
                  PLUM_HAL_UART_IDX(id));
            break;
    }

    return (port);
}

PLUM_PRIVATE
uart_word_length_t hal_esp32_uart_data_bit_trans(
    plum_hal_uart_databit_t databit)
{
    uart_word_length_t length = UART_DATA_BITS_MAX;

    switch (databit) {
        case PLUM_HAL_UART_DATABIT_5:
            length = UART_DATA_5_BITS;
            break;

        case PLUM_HAL_UART_DATABIT_6:
            length = UART_DATA_6_BITS;
            break;

        case PLUM_HAL_UART_DATABIT_7:
            length = UART_DATA_7_BITS;
            break;

        case PLUM_HAL_UART_DATABIT_8:
            length = UART_DATA_8_BITS;
            break;

        default:
            break;
    }

    return (length);
}

PLUM_PRIVATE
uart_parity_t hal_esp32_uart_parity_trans(plum_hal_uart_parity_t parity)
{
    uart_parity_t pty = UART_PARITY_DISABLE;

    switch (parity) {
        case PLUM_HAL_UART_PARITY_NONE:
            pty = UART_PARITY_DISABLE;
            break;

        case PLUM_HAL_UART_PARITY_EVEN:
            pty = UART_PARITY_EVEN;
            break;

        case PLUM_HAL_UART_PARITY_ODD:
            pty = UART_PARITY_ODD;
            break;

        default:
            break;
    }

    return (pty);
}

PLUM_PRIVATE
uart_stop_bits_t hal_esp32_uart_stop_bits_trans(plum_hal_uart_stopbit_t stopbit)
{
    uart_stop_bits_t stop_bits = UART_STOP_BITS_MAX;

    switch (stopbit) {
        case PLUM_HAL_UART_STOPBIT_1:
            stop_bits = UART_STOP_BITS_1;
            break;

        case PLUM_HAL_UART_STOPBIT_1_5:
            stop_bits = UART_STOP_BITS_1_5;
            break;

        case PLUM_HAL_UART_STOPBIT_2:
            stop_bits = UART_STOP_BITS_2;
            break;

        default:
            break;
    }

    return (stop_bits);
}

PLUM_PRIVATE
uart_hw_flowcontrol_t hal_esp32_uart_flow_ctrl_trans(
    plum_hal_uart_flow_ctrl_t flow_ctrl)
{
    uart_hw_flowcontrol_t flowctrl = UART_HW_FLOWCTRL_MAX;

    switch (flow_ctrl) {
        case PLUM_HAL_UART_FLOW_CTRL_NONE:
            flowctrl = UART_HW_FLOWCTRL_DISABLE;
            break;

        case PLUM_HAL_UART_FLOW_CTRL_CTS:
            flowctrl = UART_HW_FLOWCTRL_CTS;
            break;

        case PLUM_HAL_UART_FLOW_CTRL_RTS:
            flowctrl = UART_HW_FLOWCTRL_RTS;
            break;

        case PLUM_HAL_UART_FLOW_CTRL_RTS_CTS:
            flowctrl = UART_HW_FLOWCTRL_CTS_RTS;
            break;

        default:
            break;
    }

    return (flowctrl);
}

/*****************************************************************************
 * Public Functions
 ****************************************************************************/

PLUM_PUBLIC
plum_s32 plum_hal_uart_init(plum_u32 id, plum_hal_uart_cof_t *config)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!PLUM_HAL_ID_IS_UART(id)) {
            LOG_E("the ID is invalid");
            rc = PLUM_ECODE_INVALID;
            break;
        }

        uart_port_t port = hal_esp32_uart_port(id);
        if (port == HAL_ESP32_UART_PORT_DEFALT) {
            LOG_E("uart port err");
            rc = PLUM_ECODE_EPARA;
            break;
        }

        uart_config_t uart_cof = {};
        uart_cof.baud_rate     = config->baudrate;
        uart_cof.source_clk    = UART_SCLK_APB;
        uart_cof.data_bits     = hal_esp32_uart_data_bit_trans(config->databit);
        uart_cof.parity        = hal_esp32_uart_parity_trans(config->parity);
        uart_cof.stop_bits = hal_esp32_uart_stop_bits_trans(config->stopbit);
        uart_cof.flow_ctrl = hal_esp32_uart_flow_ctrl_trans(config->flowctrl);
        uart_param_config(port, &uart_cof);

        int size = (config->rxbuf_size > 128) ? config->rxbuf_size : 128;
        uart_driver_install(port, size, size, 20, NULL, 0);
        uart_set_pin(port, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE,
                     UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_hal_uart_send(plum_u32 id, PLUM_CONST plum_void *data,
                            plum_u16 cnt)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!PLUM_HAL_ID_IS_UART(id)) {
            LOG_E("the ID is invalid");
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if (!data) {
            LOG_E("the msg pointer is null");
            rc = PLUM_ECODE_INVALID;
            break;
        }

        uart_port_t port = hal_esp32_uart_port(id);
        if (port == HAL_ESP32_UART_PORT_DEFALT) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        rc = uart_write_bytes(port, data, cnt);
        if (rc < 0) {
            LOG_E("uart write msg err,rc : %d", rc);
            rc = PLUM_ECODE_EIO;
            break;
        }
        uart_wait_tx_done(port, 0xFFFFFFFF);
    } while (0);

    return (rc);
}

PLUM_PUBLIC
plum_s32 plum_hal_uart_recv(plum_u32 id, plum_void *data, plum_u16 size,
                            plum_u32 timeout)
{
    plum_s32 rc = PLUM_ECODE_OK;

    do {
        if (!PLUM_HAL_ID_IS_UART(id)) {
            LOG_E("the ID is invalid");
            rc = PLUM_ECODE_INVALID;
            break;
        }

        if (!data) {
            LOG_E("the msg pointer is null");
            rc = PLUM_ECODE_INVALID;
            break;
        }

        uart_port_t port = hal_esp32_uart_port(id);
        if (port == HAL_ESP32_UART_PORT_DEFALT) {
            rc = PLUM_ECODE_EPARA;
            break;
        }

        rc = uart_read_bytes(port, data, size, timeout);
        if (rc < 0) {
            LOG_E("uart read msg err,rc : %d", rc);
            rc = PLUM_ECODE_EIO;
            break;
        }

    } while (0);

    return (rc);
}

/****************************************************************************/
/*                                                                          */
/*  End of file.                                                            */
/*                                                                          */
/****************************************************************************/
